#include "board.h"
#include "tusb.h"
#include "z80.h"

// IRQ
bool IRQ = false;

// Data from CPLD.
volatile int IRQ_Data = 0;

// Peripheral address.
uint8_t Z80_Addr = 0;

// Data written by z80 to the peripheral.
uint8_t Z80_WData = 0;

// Data written by peripheral to the z80.
uint8_t Z80_RData = 0;

// Z80 write operation.
uint8_t Z80_WriteOperation = 0;

// GPIO4_0 ISR
void GPIO4_isr()
{
    if(GPIO_IsRawIntActive(GPIO4, GPIO_BIT0)) {
        GPIO_ClearInt(GPIO4, GPIO_BIT0);
        __Z80_IRQ();
    }
}

void Z80_Init()
{    
    // Set up the GPIO interrupt
    SYS_EnableAPBClock(APB_MASK_GPIO4);
    GPIO_SetInput(GPIO4, GPIO_BIT0);
    GPIO_IntConfig(GPIO4, GPIO_BIT0, GPIO_INTMODE_FALLEDGE);
    GPIO_EnableInt(GPIO4, GPIO_BIT0);
    INT_EnableIRQ(GPIO4_IRQn, 6);

    // Pull the /RESET signal high to reset the CPU
    *(int*)REG_RESET = 0;
}

bool Z80_Poll(PollFd* fd)
{
    if (!fd || !IRQ) {
        return false;
    }
    IRQ = false;
    fd->event = Z80_WriteOperation ? IO_WRITE : IO_READ;
    fd->addr = Z80_Addr;
    fd->wdata = &Z80_WData;
    fd->rdata = &Z80_RData;
    return true;
}

void Z80_Finish()
{
    // Write result to z80.
    if (!Z80_WriteOperation) {
        *(volatile int*)REG_DATA = Z80_RData;
    }

    // Pull the Z80 /WAIT pin high to exit the wait mode.
    *(volatile int*)REG_WAIT = 0;

    // Clean
    IRQ_Data = 0;
}

inline void __Z80_IRQ()
{
    IRQ = true;
    IRQ_Data = *(volatile int*)REG_DATA;
    Z80_Addr  = (uint8_t)(IRQ_Data >> 8 & 0xFF);
    Z80_WData = (uint8_t)(IRQ_Data >> 0 & 0xFF);
    Z80_WriteOperation = (IRQ_Data & 0x80000000) ? 1 : 0;
}